package bancosys.tec.persist.dao;

import java.lang.reflect.Method;

import jmine.tec.annotations.Unmodifiable;
import jmine.tec.persist.PersistMessages;

import org.hibernate.Criteria;
import org.hibernate.criterion.Restrictions;

import bancosys.tec.persist.Persistable;
import bancosys.tec.persist.SystemEntityIdentifier;
import bancosys.tec.persist.exception.PersistenceException;
import bancosys.tec.persist.persister.annotation.SystemId;

/**
 * DAO for entities that have a field annotated with SystemId. This field should identify system entities, needed for the system to work.
 * {@link SystemId}
 * 
 * @author lundberg
 * @param <B> Managed entity class
 * @param <I> SystemEntityIdentifier class
 */
@Unmodifiable
public class SystemEntityDAO<B extends Persistable, I extends SystemEntityIdentifier<?>> extends BaseDAO<B> {

    private static final String PREFIX_IS = "is";

    private static final String PREFIX_GET = "get";

    private String systemIdentifierProperty = null;

    /**
     * Finds a system entity, given a SystemEntityIdentifier. This entity is expected to be found. If it is not found, the database is
     * invalid.
     * 
     * @param systemEntityIdentifier systemEntityIdentifier
     * @return BoClass
     */
    public B findBySystemIdentifier(I systemEntityIdentifier) {
        Criteria criteria = this.createCriteria();
        criteria.add(Restrictions.eq(this.getSystemIdentifierProperty(), systemEntityIdentifier.getSystemId()));
        try {
            return this.executeSingleQuery(criteria);
        } catch (BeanNotFoundException e) {
            throw new SystemEntityNotFoundException(PersistMessages.SYSTEM_ENTITY_NOT_FOUND.create(this.getPersistableClass().getName(),
                    systemEntityIdentifier.getSystemId()), e);
        }
    }

    /**
     * Returns the name of the entity's SystemId property.
     * 
     * @return property name
     */
    private String getSystemIdentifierProperty() {
        if (this.systemIdentifierProperty == null) {
            Class<? extends B> klass = this.getPersistableClass();
            for (Method method : klass.getMethods()) {
                if (method.isAnnotationPresent(SystemId.class)) {
                    String methodName = method.getName();
                    String propertyNameRaw =
                            methodName.startsWith(PREFIX_GET) ? methodName.substring(PREFIX_GET.length()) : methodName.substring(PREFIX_IS
                                    .length());
                    this.systemIdentifierProperty = propertyNameRaw.substring(0, 1).toLowerCase() + propertyNameRaw.substring(1);
                    return this.systemIdentifierProperty;
                }
            }
            throw new PersistenceException(PersistMessages.SYSTEM_ID_PROPERTY_NOT_FOUND.create(this.getPersistableClass().getName()));
        }
        return this.systemIdentifierProperty;
    }
}
